/**
 * @file Tuple.cpp
 * 
 * This file contains the definitions of all the funcitons to manipulate the 
 * Tuple class.
 */
#include "Tuple.hpp"
#include <iostream>
#include <cmath>
using namespace math;

using std::ostream;

/**
 * Default constructor
 */
Tuple::Tuple()
{
	sizeTuple = 0;
	elementTuple = new double[1];
	elementTuple[sizeTuple] = 0;
}

/**
 * Copy constructor 
 * @param name: tuple - description:a tuple passed by reference.
 */
Tuple::Tuple( const Tuple& tuple )
{
	sizeTuple = tuple.sizeTuple;
	elementTuple = new double[sizeTuple];
	for( int i = 0; i < sizeTuple; i++ )
		elementTuple[i] = tuple.elementTuple[i];
}

/**
 * Construct for specifying only the size
 * @param name: size - description: a variable passed by the user.
 */
Tuple::Tuple( unsigned int size )
{
	sizeTuple = size;
	elementTuple = new double[sizeTuple];
	for( int i = 0; i < sizeTuple; i++ )
		elementTuple[i] = 0;
}

/**
 * Construct for specifying the size and the elements of the Tuple
 * @param name: size, tuple[] - description: a variable and and array
 * given by the user.
 */
Tuple::Tuple( unsigned int size, const double tuple[] )
{
	sizeTuple = size;
	elementTuple = new double[sizeTuple];
	for( int i = 0; i < sizeTuple; i++ )
		elementTuple[i] = tuple[i];
}

/**
 * Destructor
 */
Tuple::~Tuple()
{
	delete[] elementTuple;
}

/**
 * Returns the size of a Tuple
 */
unsigned int Tuple::getSize() const
{
	return sizeTuple;
}

/**
 * Returns the sum of the produtcs of the corresponding elements
 * of two Tuples.
 * @param name: tuple - description: a Tuple passed by reference.
 */
double Tuple::dot( const Tuple& tuple ) const
{
	double result = 0;
	if( sizeTuple != tuple.sizeTuple )
		throw SizeMismatch( "Tuples are not the same size!" );
	else
		for( int i =0; i < sizeTuple; i++ )
			result += elementTuple[i] * tuple.elementTuple[i];

	return result;
}

/**
 * Returns the square root of the dot product of a Tuple.
 */
double Tuple::length() const
{
	double result = 0;
	for( int i =0; i < sizeTuple; i++ )
		result += elementTuple[i] * elementTuple[i];

	return sqrt( result );
}

/**
 * Overloads the index operator in order to change a slot in the Tuple.
 * @param name: t - description: an integer passed by the user.
 */
double& Tuple::operator[]( unsigned int t )
{
	if( t >= sizeTuple )
		throw OutOfBounds( "Slot is out of bounds!" );
	else
		return elementTuple[t];
}

/**
 * Overloads the index operator in order to extract a specific slot in the Tuple.
 * @param name: t - description: an integer passed by the user.
 */
const double& Tuple::operator[]( unsigned int t ) const
{
	if( t >= sizeTuple )
		throw OutOfBounds( "Slot is out of bounds!" );
	else
		return elementTuple[t];
}

/**
 * Shows the compiler how to print a Tuple
 * @param name: out - description: an ostream reference variable.
 */
void Tuple::print( ostream& out ) const
{
	out << "[";
	for( int i = 0; i < sizeTuple; i++ )
	{
		if( i == sizeTuple - 1 )
			out << elementTuple[i]; 
		else
			out << elementTuple[i] << " ";
	}
    out << "]";
}

/**
 * Overloads the assignment operator.
 * @param name: tuple - description: a Tuple passed by the user.
 */
Tuple& Tuple::operator=( const Tuple& tuple )
{
	if( this != &tuple )
	{
		delete[] elementTuple;
		sizeTuple = tuple.sizeTuple;
		elementTuple = new double[sizeTuple];
		for( int i = 0; i < sizeTuple; i++ )
		{
			elementTuple[i] = tuple.elementTuple[i];
		}
	}
	return *this;
}

/**
 * Returns true if two Tuples are equal to each other
 * @param name: tuple - description: a Tuple passed by the user.
 */
bool Tuple::operator==( const Tuple& tuple ) const
{
	if(sizeTuple == tuple.sizeTuple)
	{
		for( int i = 0; i < sizeTuple; i++ )
			if( elementTuple[i] != tuple.elementTuple[i] )
				return false;

		return true;
	}
	else
		return false;
}

/**
 * Returns true if two Tuples are not equal to each other
 * @param name: tuple - description: a Tuple passed by the user.
 */
bool Tuple::operator!=( const Tuple& tuple ) const
{
	if(sizeTuple == tuple.sizeTuple)
	{
		for( int i = 0; i < sizeTuple; i++ )
			if( elementTuple[i] != tuple.elementTuple[i] )
				return true;

		return false;
	}
	else
		return true;
}

/**
 * Overloads the incrementor operator.
 * @param name: tuple - description: a Tuple passed by the user.
 */
Tuple& Tuple::operator+=( const Tuple& tuple )
{
	if( sizeTuple != tuple.sizeTuple )
		throw SizeMismatch( "Tuples are not the same size!" );
	else
		for( int i = 0; i < sizeTuple; i++ )
			elementTuple[i] = elementTuple[i] + tuple.elementTuple[i];

	return *this;
}

/**
 * Overloads the decrementor operator.
 * @param name: tuple - description: a Tuple passed by the user.
 */
Tuple& Tuple::operator-=( const Tuple& tuple )
{
	if( sizeTuple != tuple.sizeTuple )
		throw SizeMismatch( "Tuples are not the same size!" );
	else
		for( int i = 0; i < sizeTuple; i++ )
			elementTuple[i] = elementTuple[i] - tuple.elementTuple[i];

	return *this;
}

/**
 * Overloads the addition operator.
 * @param name: tuple - description: a Tuple passed by the user.
 */
Tuple Tuple::operator+( const Tuple& tuple ) const
{
	Tuple result( *this );
	if( sizeTuple != tuple.sizeTuple )
		throw SizeMismatch( "Tuples are not the same size!" );
	else
		for( int i = 0; i < sizeTuple; i++ )
			result.elementTuple[i] = elementTuple[i] + tuple.elementTuple[i];

	return result;
}

/**
 * Overloads the subtraction operator.
 * @param name: tuple - description: a Tuple passed by the user.
 */
Tuple Tuple::operator-( const Tuple& tuple ) const
{
	Tuple result( *this );
	if( sizeTuple != tuple.sizeTuple )
		throw SizeMismatch( "Tuples are not the same size!" );
	else
		for( int i = 0; i < sizeTuple; i++ )
			result.elementTuple[i] = elementTuple[i] - tuple.elementTuple[i];

	return result;
}

/**
 * Overloads the multiplication operator.
 * @param name: t - description: a double reference variable.
 */
Tuple Tuple::operator*( const double& t ) const
{
	Tuple result( *this );
	for( int i = 0; i < sizeTuple; i++ )
		result.elementTuple[i] = elementTuple[i] * t;

	return result;
}

/**
 * Overloads the division operator.
 * @param name: t - description: a double reference variable.
 */
Tuple Tuple::operator/( const double& t ) const
{
	Tuple result( *this );
	for( int i = 0; i < sizeTuple; i++ )
		result.elementTuple[i] = elementTuple[i] / t;

	return result;
}

/**
 * Overloads the negation operator.
 */
Tuple Tuple::operator-() const
{
	Tuple result( *this );

	return ( result * -1 );
}

/**
 * Overloads the multiplication operator.
 * @param name: t - description: a double reference variable.
 */
Tuple Tuple::operator*=( const double& t ) const
{
	for( int i = 0; i < sizeTuple; i++ )
		elementTuple[i] = elementTuple[i] * t;

	return *this;
}

/**
 * Overloads the division operator.
 * @param name: t - description: a double reference variable.
 */
Tuple Tuple::operator/=( const double& t ) const
{
	for( int i = 0; i < sizeTuple; i++ )
		elementTuple[i] = elementTuple[i] / t;

	return *this;
}

/**
 * Overloads the multiplication operator to be used when a nonmember is
 * being multiplied by a Tuple.
 * @param name: t - description: a double reference variable.
 */
Tuple math::operator*( const double t, const Tuple& tuple )
{
	return (tuple * t);
}

/**
 * Exception constructor to be used when two Tuples are not the same size.
 * @param name: message - description: a string that contains a specific message.
 */
Tuple::SizeMismatch::SizeMismatch( std::string message )
{
	error_message = message;
}

/**
 * Returns the specific error message generated when two Tuples are not the same
 * size.
 */
const char* Tuple::SizeMismatch::what() const
{
	return error_message.c_str();
}

/**
 * Exception constructor to be used when an index is less then or greater the
 * the size of a Tuple.
 * @param name: message - description: a string that contains a specific message.
 */
Tuple::OutOfBounds::OutOfBounds( std:: string message )
{
	error_message = message;
}

/**
 * Returns the specific error message generated by the invalid index input.
 */
const char* Tuple::OutOfBounds::what() const
{
	return error_message.c_str();
}